package com.zcs.gateway.filters;

import com.zcs.gateway.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author zhangchunsheng
 * @version 1.0.0
 * @className AuthGlobalFilter
 * @date 2024/11/13   17:39
 * @description TODO
 */
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {


    @Autowired
    private Environment env;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private static final String TOKEN_HEADER_KEY = "auth_token";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1.获取请求对象
        ServerHttpRequest request = exchange.getRequest();
        // 2.判断是否不需要拦截
        if(isLoginRequiredForPath(request.getPath().toString())){
            // 无需拦截，直接放行
            return chain.filter(exchange);
        }
        // 3.获取token
        String token = getToken(request);
        ServerHttpResponse response = exchange.getResponse();
        if (StringUtils.isBlank(token)) {
            // 4.token为空 返回401
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        // 5.验证token是否有效
        String userId = getUserIdByToken(token);
        if (StringUtils.isBlank(userId)) {
            // 5.token无效 返回401
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        // token有效，后续业务处理

        // 从写请求头，方便业务系统从请求头获取用户id进行权限相关处理
        ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
        request = builder.header("user_id", userId).build();
        // 延长缓存过期时间-token缓存用户如果一直在操作就会一直重置过期
        // 这样避免用户操作过程中突然过期影响业务操作及体验，只有用户操作间隔时间大于缓存过期时间才会过期
        resetTokenExpirationTime(token, userId);
        // 完成验证
        return chain.filter(exchange);
    }


    @Override
    public int getOrder() {
        // 优先级 越小越优先
        return 0;
    }

    /**
     * 从redis中获取用户id
     * 在登录操作时候 登陆成功会生成一个token, redis得key为auth_token:token 值为用户id
     *
     * @param token
     * @return
     */
    private String getUserIdByToken(String token) {
        try {
            Claims claims = JwtUtils.analysisJWT(token);
            String claimFiled = JwtUtils.getClaimFiled(token, "user-info");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }


        String redisKey = String.join(":", "auth_token", token);
        return redisTemplate.opsForValue().get(redisKey);
    }

    /**
     * 重置token过期时间
     *
     * @param token
     * @param userId
     */
    private void resetTokenExpirationTime(String token, String userId) {
        String redisKey = String.join(":", "auth_token", token);
        redisTemplate.opsForValue().set(redisKey, userId, 2, TimeUnit.HOURS);
    }


    /**
     * 获取token
     *
     * @param request
     * @return
     */
    private static String getToken(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        // 从请求头获取token
        String token = headers.getFirst(TOKEN_HEADER_KEY);
        if (StringUtils.isBlank(token)) {
            // 请求头无token则从url获取token
            token = request.getQueryParams().getFirst(TOKEN_HEADER_KEY);
        }
        if (StringUtils.isBlank(token)) {
            // 请求头和url都没有token则从cookies获取
            HttpCookie cookie = request.getCookies().getFirst(TOKEN_HEADER_KEY);
            if (cookie != null) {
                token = cookie.getValue();
            }
        }
        return token;
    }


    /**
     * 判断传入的地址是否需要登录
     *
     * @param path
     * @return
     */
    private boolean isLoginRequiredForPath(String path) {
        String excludePathsStr = env.getProperty("spring.security.auth.excludePaths");
        List<String> excludePaths = Arrays.asList(excludePathsStr.split(","));
        for (String pattern : excludePaths) {
            if (path.equals(pattern)) {
                return true;
            }
        }
        return false;
    }
    /**
     * 判断该请求是否已经登录
     *
     * @param request
     * @return
     */
    private boolean isAuth(ServerHttpRequest request) {
        //获取请求头中的token
        List<String> headers = request.getHeaders().get("Token");
        String token = "";
        if (!CollectionUtils.isEmpty(headers)) {
            token = headers.get(0);
        }
        if (StringUtils.isBlank(token)) {
            return false;
        }
        //校验token
        return true;
//        return AuthUtil.isAuth(token);
    }
}


